﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Neits.ConfigurationService;
using Projecturo.ProjecturoClient.Connection;

namespace Projecturo.ProjecturoClient.ViewModel
{
    public class ViewModelManager
    {
        private static object lockObject = new object();
        private static ViewModelManager instance;

        private Dictionary<Guid, ViewModelBase> viewModels = new Dictionary<Guid, ViewModelBase>();
        private ViewModelManagerConfiguration configuration;
        private IConfigurationService configurationService;

        private ViewModelManager()
        {
        }

        private IConfigurationService ConfigurationService
        {
            get
            {
                if (configurationService == null)
                {
                    configurationService = Connector.UnityContainer.Resolve<IConfigurationService>();
                    configurationService = Connector.UnityContainer.BuildUp(configurationService);
                }
                return configurationService;
            }
        }

        private ViewModelManagerConfiguration Configuration
        {
            get
            {
                if (configuration == null)
                {
                    configuration = ConfigurationService.GetConfiguration<ViewModelManagerConfiguration>();
                }
                return configuration;
            }
        }

        public static ViewModelManager Instance
        {
            get
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new ViewModelManager();
                    }
                }
                return instance;
            }
        }

        public ViewModelBase CreateViewModel(string viewModelName)
        {
            Type viewModelType = Configuration.ViewModels.GetViewModelType(viewModelName);
            return BuildViewModel(viewModelType);
        }

        public ViewModelBase CreateViewModel(string viewModelName, ViewModelBase parent)
        {
            Type viewModelType = Configuration.ViewModels.GetViewModelType(viewModelName);
            ViewModelBase viewModel = BuildViewModel(viewModelType);
            if (parent != null)
                viewModel.ViewModelReference.ParentViewModelId = parent.ViewModelReference.CurrentViewModelId;
            return viewModel;
        }

        public ViewModelBase CreateViewModel(Type modelType)
        {
            Type viewModelType = Configuration.ViewModels.GetViewModelType(modelType);
            return BuildViewModel(viewModelType);
        }

        private ViewModelBase BuildViewModel(Type viewModelType)
        {
            var viewModelBase = Connector.UnityContainer.Resolve(viewModelType) as ViewModelBase;
            if (viewModelBase != null)
            {
                viewModelBase = Connector.UnityContainer.BuildUp(viewModelBase);
                RegisterViewModel(viewModelBase);

                return viewModelBase;
            }
            return null;
        }

        void OnViewModelCloseRequested(object sender, EventArgs e)
        {
            ViewModelBase viewModelBase = sender as ViewModelBase;
            if (viewModelBase != null)
            {
                // TODO: Update Release-Management
                //this.ReleaseViewModel(viewModelBase.ViewModelReference.CurrentViewModelId);
            }
        }

        public ViewModelBase CreateViewModel(Type modelType, ViewModelBase parent)
        {
            ViewModelBase viewModelBase = CreateViewModel(modelType);
            if (viewModelBase != null)
            {
                viewModelBase.ViewModelReference.ParentViewModelId = parent.ViewModelReference.CurrentViewModelId;
            }
            return viewModelBase;
        }

        public T CreateViewModel<T>() where T : ViewModelBase
        {
            ViewModelBase viewModelBase = Connector.UnityContainer.Resolve<T>();
            if (viewModelBase != null)
            {
                viewModelBase = Connector.UnityContainer.BuildUp(viewModelBase);
                RegisterViewModel(viewModelBase);
                return (T)viewModelBase;
            }
            return null;
        }

        public T CreateViewModel<T>(ViewModelBase parent) where T : ViewModelBase
        {
            T viewModelBase = CreateViewModel<T>();
            if (viewModelBase != null)
                viewModelBase.ViewModelReference.ParentViewModelId = parent.ViewModelReference.CurrentViewModelId;
            return viewModelBase;
        }

        public ViewModelBase GetViewModel(Guid id)
        {
            if (viewModels.ContainsKey(id))
                return viewModels[id];
            return null;
        }

        public List<ViewModelBase> GetChildViewModels(Guid parentId)
        {
            var childViewModels = new List<ViewModelBase>();
            Dictionary<Guid,ViewModelBase>.Enumerator viewModelEnumerator = viewModels.GetEnumerator();
            while(viewModelEnumerator.MoveNext())
            {
                if (viewModelEnumerator.Current.Value.ViewModelReference.ParentViewModelId.Equals(parentId))
                    childViewModels.Add(viewModelEnumerator.Current.Value);
            }
            return childViewModels;
        }

        public void ReleaseViewModel(ViewModelBase viewModel)
        {
            ReleaseViewModel(viewModel.ViewModelReference.CurrentViewModelId);
        }

        public void ReleaseViewModel(Guid id)
        {
            if (viewModels.ContainsKey(id))
            {
                ViewModelBase viewModelBase = viewModels[id];
                viewModelBase.CloseRequested -= new EventHandler(OnViewModelCloseRequested);
                viewModels.Remove(id);
                Debug.WriteLine("ViewModel released " + viewModelBase.ViewModelReference.CurrentViewModelId.ToString());
            }
        }

        private void RegisterViewModel(ViewModelBase viewModelBase)
        {
            viewModelBase.ViewModelReference.CurrentViewModelId = Guid.NewGuid();
            viewModelBase.CloseRequested += new EventHandler(OnViewModelCloseRequested);
            this.viewModels.Add(viewModelBase.ViewModelReference.CurrentViewModelId, viewModelBase);
            Debug.WriteLine(String.Format("ViewModel {0} registered", viewModelBase.ViewModelReference.CurrentViewModelId));
            viewModelBase.SetUp();
        }
    }
}
